﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SwaggerSettingsDemo.SwaggerExt
{
    /// <summary>
    /// use annotation [ApiExplorerSettings(GroupName =nameof(ApiGroupName.WeatherForecast))] to make one controller belong to which group
    /// </summary>
    public enum ApiGroupName
    {
        Test = 0,
        WeatherForecast = 1
    }

    public static class SwaggerExtension
    {
        #region 基本配置实现基础功能：分组，权限验证
        //参考文档：https://blog.csdn.net/qq_53123067/article/details/144408118

        /// <summary>
        /// 注册并配置Swagger服务
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection ConfigureBasicSwaggerServices(this IServiceCollection services)
        {
            services.AddSwaggerGen(opt =>
            {
                #region 添加注释信息
                //解析站点下所有xml，一般加自己项目的引用的即可
                foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "*.xml"))
                {
                    opt.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));
                }
                #endregion

                #region 添加swagger版本支持
                foreach (var groupName in Enum.GetNames(typeof(ApiGroupName)))
                {
                    opt.SwaggerDoc(groupName, new OpenApiInfo()
                    {
                        Title = $"API {groupName}",
                        Version = "v1",
                        Description = $"API {groupName} 版本"
                    });
                }
                #endregion

                #region 支持权限验证
                opt.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme()
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"", // 描述
                    Name = "Authorization", // 授权名称
                    In = ParameterLocation.Header, // 授权位置，放在头信息进行传值
                    Type = SecuritySchemeType.Http, // 授权类型
                    BearerFormat = "JWT", // 格式是JWT
                    Scheme = JwtBearerDefaults.AuthenticationScheme
                });
                // 添加安全要求
                opt.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference()
                            {
                                Id = JwtBearerDefaults.AuthenticationScheme, // 必须和上面一致
                                Type = ReferenceType.SecurityScheme
                            }
                        },
                        new string[] { }
                    }
                });
                #endregion
            });
            return services;

        }

        /// <summary>
        /// 开启Swagger中间件
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder StartBasicSwagger(this IApplicationBuilder app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(opt =>
            {
                foreach (var groupName in Enum.GetNames(typeof(ApiGroupName)))
                {
                    opt.SwaggerEndpoint($"/swagger/{groupName}/swagger.json", groupName);
                }
            });
            return app;
        }
        #endregion

        #region 根据我们的自定义特性生成多个文档
        //参考文档：https://blog.csdn.net/qq_31463571/article/details/141166730
        //https://www.cnblogs.com/caijt/p/10739841.html

        public static IServiceCollection ConfigureSwaggerServicesByCustomAttribute(this IServiceCollection services)
        {
            services.AddSwaggerGen(options =>
            {
                // 根据模块和版本生成多个文档
                var apiAssembly = Assembly.GetExecutingAssembly();
                var apiDescriptions = apiAssembly.GetTypes()
                    .Where(t => t.GetCustomAttributes<ApiDescriptionAttribute>().Any())
                    .Select(t => t.GetCustomAttribute<ApiDescriptionAttribute>())
                    .Distinct();

                foreach (var desc in apiDescriptions)
                {
                    if (desc != null)
                    {
                        if (string.IsNullOrEmpty(desc.Version))
                        {
                            options.SwaggerDoc($"{desc.Title}", new OpenApiInfo { Title = $"{desc.Title} API", Version = desc.Version, Description = desc.Description, });
                        }
                        else
                        {
                            options.SwaggerDoc($"{desc.Title}-{desc.Version}", new OpenApiInfo
                            {
                                Title = $"{desc.Title} API",
                                Version = desc.Version,
                                Description = desc.Description,
                            });
                        }
                    }
                }
                //没有加特性的分到这个NoGroup上
                options.SwaggerDoc("NoGroup", new OpenApiInfo
                {
                    Title = "无分组"
                });
                //判断接口归于哪个分组
                options.DocInclusionPredicate((docName, apiDescription) =>
                {
                    if (docName == "NoGroup")
                    {
                        //当分组为NoGroup时，只要没加特性的都属于这个组
                        return string.IsNullOrEmpty(apiDescription.GroupName);
                    }
                    else
                    {
                        return apiDescription.GroupName == docName;
                    }
                });
            });

            return services;
        }

        public static IApplicationBuilder StartSwaggerWithCustomAttribute(this IApplicationBuilder app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                // 根据模块和版本生成多个文档
                var apiAssembly = Assembly.GetExecutingAssembly();
                var apiDescriptions = apiAssembly.GetTypes()
                    .Where(t => t.GetCustomAttributes<ApiDescriptionAttribute>().Any())
                    .Select(t => t.GetCustomAttribute<ApiDescriptionAttribute>())
                    .OrderBy(t => t?.Position ?? int.MaxValue).ThenBy(t => t?.Title).ThenBy(t => t?.Version)
                    .Distinct();

                foreach (var desc in apiDescriptions)
                {
                    if (desc != null)
                    {
                        if (string.IsNullOrEmpty(desc.Version))
                        {
                            options.SwaggerEndpoint($"/swagger/{desc.Title}/swagger.json", $"{desc.Title} API");
                        }
                        else
                        {
                            options.SwaggerEndpoint($"/swagger/{desc.Title}-{desc.Version}/swagger.json", $"{desc.Title} API {desc.Version}");
                        }
                    }
                }

                options.SwaggerEndpoint("/swagger/NoGroup/swagger.json", "无分组");
            });

            return app;
        }
        #endregion

        #region 使用Endpoint导航 Swagger访问路径，并根据命名空间生成多个文档

        private static readonly Dictionary<string, string> GroupApisDic = new Dictionary<string, string>() {
            { "SwaggerSettingsDemo.Controllers.ModuleA" ,"Module A APIs"},
            { "SwaggerSettingsDemo.Controllers.ModuleB","Module B APIs" }
        };

        private static readonly string Api_Version = "v1";

        public static IServiceCollection ConfigureSwaggerServicesByNamespace(this IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                //配置文档信息
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "DaX项目接口文档",
                    Description = "低代码平台API",
                    Version = Api_Version,
                });
                //配置环境
                c.AddServer(new OpenApiServer()
                {
                    Url = "",
                    Description = "BSoft Dev Server",
                });
                //配置模型标识，默认type.Name,名称一样，不同明明空间会报错，所以改成FullName,加上命名空间区分
                c.CustomSchemaIds(type => type.FullName);
                //配置唯一标识
                c.CustomOperationIds(apiDesc =>
                {
                    var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
                    return controllerAction?.ControllerName + "-" + controllerAction?.ActionName;
                });
                //解析站点下所有xml，一般加自己项目的引用的即可
                foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "*.xml"))
                {
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));
                }

                //增加模块接口的注册
                GroupApisDic.Keys.ToList().ForEach(s =>
                {
                    c.SwaggerDoc(s, new OpenApiInfo
                    {
                        Title = "低代码平台 - " + GroupApisDic[s],
                        Description = "低代码平台 " + GroupApisDic[s],
                        Version = Api_Version,
                    });
                });

                ////启用数据注解
                //c.EnableAnnotations(true, true);

                //自定义分组匹配
                c.DocInclusionPredicate((docName, apiDes) =>
                {
                    if (GroupApisDic.Keys.Contains(docName))
                    {
                        var displayName = apiDes.ActionDescriptor?.DisplayName?.ToLower() ?? string.Empty;
                        var existGroup = GroupApisDic.Keys.FirstOrDefault(s => displayName.Contains(s.ToLower()));
                        return docName == existGroup;
                    }

                    return true;
                });
            });

            return services;
        }
        public static IApplicationBuilder StartSwaggerWithNamespace(this IApplicationBuilder app)
        {
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");

                //默认页支持分组
                GroupApisDic.Keys.ToList().ForEach(s =>
                {
                    c.SwaggerEndpoint($"/{s}/api-docs", GroupApisDic[s]);
                });
            });

            //单独的页面
            GroupApisDic.Keys.ToList().ForEach(s =>
            {
                app.UseSwaggerUI(c =>
                {
                    c.RoutePrefix = s;
                    c.SwaggerEndpoint($"/{s}/api-docs", GroupApisDic[s]);
                });
            });

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapSwagger("{documentName}/api-docs");
                endpoints.MapGet("/v3/api-docs/swagger-config", async (httpContext) =>
                {
                    JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
                    {
                        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
                    };
                    jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false));
                    //SwaggerUIOptions _options = new SwaggerUIOptions()
                    //{
                    //    ConfigObject = new ConfigObject()
                    //    {
                    //        Urls = new List<UrlDescriptor>
                    //    {
                    //        new UrlDescriptor()
                    //        {
                    //            Url="/v1/api-docs",
                    //            Name="V1 Docs"
                    //        }
                    //    }
                    //    }
                    //};
                    SwaggerUIOptions _options = new SwaggerUIOptions()
                    {
                        ConfigObject = new ConfigObject()
                        {
                            Urls = new List<UrlDescriptor>
                            {
                                new UrlDescriptor()
                                {
                                    Url="/v1/api-docs",
                                    Name="V1 Docs"
                                }
                            }.Concat(GroupApisDic.Keys.Select(s => new UrlDescriptor()
                            {
                                Url = $"/{s}/api-docs",
                                Name = s
                            }).ToList())
                        }
                    };

                    await httpContext.Response.WriteAsync(JsonSerializer.Serialize(_options.ConfigObject, jsonSerializerOptions));
                });

            });
            return app;
        }

        #endregion

        #region 使用SwaggerUI导航访问路径，并根据命名空间分组
        //参考文档：https://blog.csdn.net/qq_53123067/article/details/144408118

        public static IServiceCollection ConfigureBasicSwaggerServicesUsingNamespace(this IServiceCollection services)
        {
            services.AddSwaggerGen(opt =>
            {
                #region 添加注释信息
                //解析站点下所有xml，一般加自己项目的引用的即可
                foreach (var file in Directory.GetFiles(AppContext.BaseDirectory, "*.xml"))
                {
                    opt.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, file));
                }
                #endregion

                #region 添加swagger版本支持
                foreach (var groupName in GroupApisDic.Keys.ToList())
                {
                    opt.SwaggerDoc(groupName, new OpenApiInfo()
                    {
                        Title = $"API {groupName}",
                        Version = "v1",
                        Description = $"API {groupName} 版本"
                    });
                }
                #endregion

                #region 支持权限验证
                opt.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme()
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 参数结构: \"Authorization: Bearer {token}\"", // 描述
                    Name = "Authorization", // 授权名称
                    In = ParameterLocation.Header, // 授权位置，放在头信息进行传值
                    Type = SecuritySchemeType.Http, // 授权类型
                    BearerFormat = "JWT", // 格式是JWT
                    Scheme = JwtBearerDefaults.AuthenticationScheme
                });
                // 添加安全要求
                opt.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference()
                            {
                                Id = JwtBearerDefaults.AuthenticationScheme, // 必须和上面一致
                                Type = ReferenceType.SecurityScheme
                            }
                        },
                        new string[] { }
                    }
                });
                #endregion
            });
            return services;

        }

        /// <summary>
        /// 开启Swagger中间件
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder StartBasicSwaggerUsingNamespace(this IApplicationBuilder app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(opt =>
            {
                foreach (var groupName in GroupApisDic.Keys.ToList())
                {
                    //url里的groupName 必须与 SwaggerDoc()定义的name相同，否则会有404错误
                    opt.SwaggerEndpoint($"/swagger/{groupName}/swagger.json", $"{groupName} - option name");
                }
            });

            //配置分组的独立route
            foreach (var groupName in GroupApisDic.Keys.ToList())
            {
                app.UseSwaggerUI(opt =>
                {
                    opt.RoutePrefix = $"swagger/{groupName}";//最好加上swagger/,防止与其他API路径冲突
                    //url里的groupName 必须与 SwaggerDoc()定义的name相同，否则会有404错误
                    opt.SwaggerEndpoint($"/swagger/{groupName}/swagger.json", groupName);
                });
            }
                return app;
        }
        #endregion

    }
}
